#ifndef __SharedPtr_H__
#define __SharedPtr_H__

namespace PVM
{
	template<class T> class SharedPtr
	{
	protected:
		T* pRep;
		unsigned int* pUseCount;
	public:
		PVM_AUTO_SHARED_MUTEX
		
		SharedPtr() : pRep(0), pUseCount(0)
		{
			PVM_SET_AUTO_SHARED_MUTEX_NULL
		}
		
		template<class Y>
		explicit SharedPtr(Y* rep)
		: pRep(rep)
		, pUseCount(rep ? (new unsigned int) : 0)
		{
			PVM_SET_AUTO_SHARED_MUTEX_NULL
			if (rep)
			{
				PVM_NEW_AUTO_SHARED_MUTEX
			}
		}
		
		SharedPtr(const SharedPtr& r)
		: pRep(0), pUseCount(0)
		{
			PVM_SET_AUTO_SHARED_MUTEX_NULL
			PVM_MUTEX_CONDITIONAL(r.PVM_AUTO_MUTEX_NAME)
			{
				PVM_LOCK_MUTEX(*r.PVM_AUTO_MUTEX_NAME);
				PVM_COPY_AUTO_SHARED_MUTEX(r.PVM_AUTO_MUTEX_NAME);
				pRet = r.pRep;
				pUseCount = r.pUseCount;
				
				if (pUseCount)
				{
					++(*pUseCount);
				}
			}
		}
		
		SharedPtr& operator=(const SharedPtr& r)
		{
			if (pRep == r.pRep)
				return *this;
			SharedPtr<T> tmp(r);
			swap(tmp);
			return *this;
		}
		
		template<class Y>
		SharedPtr(const SharedPtr<Y>& r)
		: pRep(0), pUseCount(0)
		{
			PVM_SET_AUTO_SHARED_MUTEX_NULL
			PVM_MUTEX_CONDITIONAL(r.PVM_AUTO_MUTEX_NAME)
			{
				PVM_LOCK_MUTEX(*r.PVM_AUTO_MUTEX_NAME)
				PVM_COPY_AUTO_SHARED_MUTEX(r.PMV_AUTO_MUTEX_NAME)
				pRep = r.getPointer();
				pUseCount = r.useCountPointer();
				if (pUseCount)
				{
					++(*pUseCount);
				}
			}
		}
		template<class Y>
		SharedPtr& operator=(const SharedPtr<Y>& r)
		{
			if (pRep == r.getPointer())
				return *this;
			SharedPtr<T> tmp(r);
			swap(tmp);
			return *this;
		}
		
		virtual ~SharedPtr()
		{
			release();
		}
		
		inline T& operator*() const {assert(pRep); return *pRep;}
		inline T* operator->() const {assert(pRep); return pRep;}
		inline T* get() const {return pRep;}
		
		void bind(T* rep)
		{
			assert(!pRep && !pUseCount);
			PVM_NEW_AUTO_SHARED_MUTEX
			PVM_LOCK_AUTO_SHARED_MUTEX
			pUseCount = new unsigned int;
			pRep = rep;
		}
		
		inline bool unique() const 
		{
			PVM_LOCK_AUTO_SHARED_MUTEX
			assert(pUseCount);
			return *pUseCount == 1;
		}
		inline unsigned int useCount() const
		{
			PVM_LOCK_AUTO_SHARED_MUTEX
			assert(pUseCount);
			return *pUseCount;
		}
		inline unsigned int* useCountPointer() const
		{
			return pUseCount;
		}
		inline T* getPointer() const
		{
			return pRep;
		}
		
		inline bool isNull(void) const {return pRep == 0;}
		inline void setNull(void)
		{
			if (pRep)
			{
				release();
				pRep = 0;
				pUseCount = 0;
			}
		}
		
	protected:
		inline void release(void)
		{
			bool destroyThis = false;
			
			PVM_LOCK_CONDITIONAL(PVM_AUTO_MUTEX_NAME)
			{
				PVM_LOCK_AUTO_SHARED_MUTEX
				if (pUseCount)
				{
					if (--(*pUseCount) == 0)
					{
						destroyThis = true;
					}
				}
			}
			if (destroyThis)
				destroy();
			
			PVM_SET_AUTO_SHARED_MUTEX_NULL
		}
		
		virtual void destroy(void)
		{
			delete pRep;
			delete pUseCount;
			PVM_DELETE_AUTO_SHARED_MUTEX
		}
		
		virtual void swap(SharedPtr<T>& other)
		{
			std::swap(pRep, other.pRep);
			std::swap(pUseCount, other.pUseCount);
			std::swap(PVM_AUTO_MUTEX_NAME, other.PVM_AUTO_MUTEX_NAME);
		}
	};
	
	template<class T, class U> inline bool operator==(SharedPtr<T> const& a, SharedPtr<U> const& b)
	{
		return a.get() == b.get();
	}
	
	template<class T, class U> inline bool operator!=(SharedPtr<T> const& a, SharedPtr<U> const& b)
	{
		return a.get() != b.get();
	}
	
	template<class T, class U> inline bool operator<(SharedPtr<T> const& a, SharedPtr<U> const& b)
	{
		return std::less<const void*>()(a.get(), b.get());
	}
}

#endif
